/**
 *  TextMash - simple IDE for Clojure
 *  
 *  Copyright (C) 2010 Aleksander Naszko
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package pl.olek.textmash.workspace;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;

import pl.olek.textmash.Support;
import pl.olek.textmash.TextMash;
import pl.olek.textmash.event.Event;
import pl.olek.textmash.event.EventDispatcher;
import pl.olek.textmash.event.EventListener;
import pl.olek.textmash.matching.BracketMatcher;
import pl.olek.textmash.matching.Dictionary;
import pl.olek.textmash.matching.OccurrencesMatcher;
import pl.olek.textmash.menu.MenuBuilder;
import pl.olek.textmash.menu.WorkspaceMenu;

/**
 * Abstraction of the document being edited.
 * 
 * @author anaszko
 */
public class Workspace extends JFrame {

	private static final long serialVersionUID = 7433037006596329425L;
	Settings config;
	TextEditor content;
	String dataSource;
	LineNumbers lines;
	JScrollPane scrollPane;

	// StatusBar statusBar;

	BracketMatcher bracketMatcher;

	boolean dirty;
	boolean quickLookup;

	NoWrapEditorKit nowrapEditorKit;
	EditorKit originalEditorKit;

	public TextEditor getTextPane() {
		return content;
	}

	public Action getAction(String actionName) {
		return content.getAction(actionName);
	}

	public boolean isDirty() {
		return dirty;
	}

	public Workspace(String src) {

		EventDispatcher.getInstance().register(this, new EventListener() {

			@Override
			public void action(Event event) {
				boolean isDirty = event.getName() == EventDispatcher.WORKSPACE_IS_DIRTY;
				if (isDirty != dirty) {
					dirty = isDirty;
					setTitle(getWorkspaceTitle());
				}
			}
		}, EventDispatcher.WORKSPACE_IS_DIRTY, EventDispatcher.WORKSPACE_SAVED);

		config = Settings.getSettings(src);

		setDataSource(src);

		content = new TextEditor(this);

		ImageIcon icon = new ImageIcon(Support.getInstance().getResource(
				"pl/olek/textmash/icon16.png"));

		// statusBar = new StatusBar(this);

		setIconImage(icon.getImage());

		setJMenuBar(new WorkspaceMenu(this));
		setLayout(new BorderLayout());

		lines = new LineNumbers();

		originalEditorKit = new NormalWrapEditorKit(content);
		content.setEditorKit(originalEditorKit);
		nowrapEditorKit = new NoWrapEditorKit(content);

		content.setFont(Support.getInstance().getFont());
		content.setMargin(new Insets(0, 5, 0, 5));

		// new StatusBar(content);
		// .show();
		// content.getTo

		// content.setDoubleBuffered(true);

		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

		MonoTabSet.setTabSize(content, 4);

		bracketMatcher = new BracketMatcher(content);

		scrollPane = new JScrollPane(content);

		lines.setObserver(content, scrollPane);

		scrollPane
				.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		scrollPane
				.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scrollPane.setBorder(BorderFactory.createEmptyBorder());

		addComponentListener(new ComponentAdapter() {

			@Override
			public void componentResized(ComponentEvent e) {
				config.setNumber(Settings.WINDOW_WIDTH, e.getComponent()
						.getWidth());
				config.setNumber(Settings.WINDOW_HEIGHT, e.getComponent()
						.getHeight());
			}

			@Override
			public void componentMoved(ComponentEvent e) {

				if (getDataSource().length() != 0) {
					config
							.setNumber(Settings.WINDOW_X, e.getComponent()
									.getX());
					config
							.setNumber(Settings.WINDOW_Y, e.getComponent()
									.getY());
				}
			}

		});

		getContentPane().add(scrollPane, BorderLayout.CENTER);

		// (statusBar);

		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {

				closeApplication();
			}

			@Override
			public void windowActivated(WindowEvent e) {
				OccurrencesMatcher.getInstance().setWorkspace(Workspace.this);
				TextMash.updateOpened(false);
			}
		});

		synchronize(true);

		loadUsingEncoding(config.get(Settings.ENCODING));

		setVisible(true);

		content.discardUndoRedo();

		// content.setToolTipText("Hello World");
		// ToolTipManager.sharedInstance().setEnabled(true);
		// ToolTipManager.sharedInstance().setInitialDelay(100);
		// ToolTipManager.sharedInstance().setReshowDelay(100);
		// JToolTip tip = content.createToolTip();
		// tip.setTipText("Hello World");
		// tip.setVisible(true);

	}

	public void actionToggleVerticalSelection() {

		content
				.setVerticalSelection(config
						.toggle(Settings.VERTICAL_SELECTION));
	}

	public void actionToggleMatchBrackets() {
		if (config.toggle(Settings.MATCH_BRACKETS)) {
			bracketMatcher.turnOn();
		} else {
			bracketMatcher.turnOff();
		}
	}

	// @Override
	// public void paint(Graphics g) {
	// super.paint(g);
	//		
	// statusBar.paint(getGlassPane().getGraphics());
	// }

	public void actionToggleShowLineNumbers() {
		if (config.toggle(Settings.LINE_NUMBERS)) {
			content.setLineNumbers(lines);
			getContentPane().add(lines, BorderLayout.WEST);
		} else {
			content.setLineNumbers(null);
			getContentPane().remove(lines);
		}

		getContentPane().doLayout();
		doLayout();
		scrollPane.doLayout();

		getContentPane().repaint();
		repaint();
		scrollPane.repaint();
	}

	public void actionToggleWrapLines() {
		Document doc = content.getDocument();
		if (config.toggle(Settings.WRAP_LINES)) {
			content.setEditorKit(originalEditorKit);
		} else {
			content.setEditorKit(nowrapEditorKit);
		}
		content.setDocument(doc);
	}

	public void synchronize(boolean relocate) {

		((WorkspaceMenu) getJMenuBar()).synchronize(config, this);

		Document doc = content.getDocument();
		if (config.isOn(Settings.WRAP_LINES)) {
			if (content.getEditorKit() != originalEditorKit) {
				content.setEditorKit(originalEditorKit);
				content.setDocument(doc);
			}
		} else {
			if (content.getEditorKit() != nowrapEditorKit) {
				content.setEditorKit(nowrapEditorKit);
				content.setDocument(doc);
			}
		}

		quickLookup = config.isOn(Settings.QUICK_LOOKUP);

		content.setVerticalSelection(config.isOn(Settings.VERTICAL_SELECTION));

		if (config.isOn(Settings.LINE_NUMBERS)) {
			content.setLineNumbers(lines);
			getContentPane().add(lines, BorderLayout.WEST);
		} else {
			content.setLineNumbers(null);
			getContentPane().remove(lines);
		}

		if (config.has(Settings.LINE_ENDING)) {
			((ConcurrentDocument) content.getDocument()).setLineEnding(config
					.get(Settings.LINE_ENDING));
		}

		if (config.isOn(Settings.MATCH_BRACKETS)) {
			bracketMatcher.turnOn();
		} else {
			bracketMatcher.turnOff();
		}

		getContentPane().doLayout();
		doLayout();
		scrollPane.doLayout();

		getContentPane().repaint();
		repaint();
		scrollPane.repaint();

		if (config.has(Settings.WINDOW_WIDTH, Settings.WINDOW_HEIGHT)) {
			setSize(config.getNumber(Settings.WINDOW_WIDTH).intValue(), config
					.getNumber(Settings.WINDOW_HEIGHT).intValue());
		} else {
			Dimension dim = TextMash.getDefaultDimension();
			config.setNumber(Settings.WINDOW_WIDTH, dim.getWidth());
			config.setNumber(Settings.WINDOW_HEIGHT, dim.getHeight());
			setSize(dim);
		}

		if (getDataSource().length() == 0) {
			if (relocate) {
				Point loc = TextMash.getDefaultLocation();
				setLocation(loc);
			}
		} else {
			if (relocate) {
				if (config.has(Settings.WINDOW_X, Settings.WINDOW_Y)) {
					setLocation(config.getNumber(Settings.WINDOW_X).intValue(),
							config.getNumber(Settings.WINDOW_Y).intValue());
				} else {
					Point loc = TextMash.getDefaultLocation();
					config.setNumber(Settings.WINDOW_X, loc.getX());
					config.setNumber(Settings.WINDOW_Y, loc.getY());
					setLocation(loc);
				}
			}
		}
	}

	public boolean isUnnamed() {
		return getDataSource().length() == 0;
	}

	private void setDataSource(String name) {
		String oldDataSource = dataSource;
		dataSource = name;
		if (oldDataSource != name) {
			setTitle(getWorkspaceTitle());

			if (oldDataSource != null) {
				if (isUnnamed()) {
					// Support.getInstance().decorateWindow(this, null);
					// System.out.println("Taking new settings");
					config = Settings.getDefaultSettings();
				} else {
					// Support.getInstance().decorateWindow(this, new
					// File(dataSource));

					// System.out.println("Taking saved settings");
					config = Settings.getSettings(getDataSource());
				}

				TextMash.updateOpened(false);
			}
		}
	}

	public String getDataSource() {
		return dataSource;
	}

	private void save(String outputname) {
		try {
			File fout = new File(outputname);
			PrintStream pout = new PrintStream(fout, config
					.get(Settings.ENCODING));
			pout.print(content.getText());

			content.undo.makeAsFresh();

			/*
			 * EventDispatcher.getInstance().fire(this,
			 * EventDispatcher.WORKSPACE_SAVED);
			 */

			pout.close();

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void actionSaveAs() {
		FileDialog fileDialog = new FileDialog((Frame) null, "Save file as",
				FileDialog.SAVE);
		if (getDataSource().length() > 0) {
			File out = new File(getDataSource());
			fileDialog.setFile(out.getName());
			fileDialog.setDirectory(out.getParent());
		}
		fileDialog.setVisible(true);
		String fn = fileDialog.getFile();
		if (fn != null) {
			fn = new File(fileDialog.getDirectory(), fileDialog.getFile())
					.getAbsolutePath();
			save(fn);
			setDataSource(fn);
			TextMash.updateRecent(fn);
		}
	}

	public void actionSave() {
		if (isUnnamed()) {
			FileDialog fileDialog = new FileDialog((Frame) null, "Save file",
					FileDialog.SAVE);
			fileDialog.setVisible(true);
			String fn = fileDialog.getFile();
			if (fn != null) {
				fn = new File(fileDialog.getDirectory(), fileDialog.getFile())
						.getAbsolutePath();
				save(fn);
				setDataSource(fn);
				TextMash.updateRecent(fn);
			}
		} else {
			save(getDataSource());
		}
	}
	public String getShortWorkspaceNormalizedTitle(String value) {
		return (isUnnamed() ? value : new File(getDataSource()).getName());
	}
	public String getShortWorkspaceNormalizedTitle() {
		return getShortWorkspaceNormalizedTitle("unnamed");
	}

	public String getShortWorkspaceTitle() {
		return (dirty ? "* " : "") + getShortWorkspaceNormalizedTitle();
	}

	public String getWorkspaceTitle() {
		return (dirty ? "* " : "")
				+ MenuBuilder.truncate(Support.getInstance().getTitle(
						isUnnamed() ? "unnamed" : getDataSource()));
	}

	private void loadUsingEncoding(String encoding) {
		if (!isUnnamed()) {
			try {
				content.undo.setEnabled(false);
				// content.undo.makeAsFresh();
				content.read(new InputStreamReader(new FileInputStream(
						getDataSource()), encoding), null);

				Dictionary.getInstance().process(getDataSource(),
						content.getText());
				MonoTabSet.setTabSize(content, 4);
				content.undo.discardAllEdits();
				content.undo.setEnabled(true);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}

	public void actionUseCommentStyle(String style) {
		config.set(Settings.COMMENT, style);
	}

	public void actionUseEncoding(String newEncoding, boolean reload) {
		try {
			if (reload) {

				loadUsingEncoding(newEncoding);
			} else {

				if (content.getDocument().getLength() > 0) {
					EventDispatcher.getInstance().fire(this,
							EventDispatcher.WORKSPACE_IS_DIRTY);
				}
			}
			config.set(Settings.ENCODING, newEncoding);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void closeApplication() {

		close(true);
	}

	public void actionNew() {
		TextMash.spawnWorkspace("");
	}

	public void actionClose() {
		close(false);
	}

	public void actionOpenFile(String dataSource) {
		if (TextMash.checkIfOpened(dataSource)) {
			if (isUsed()) {
				TextMash.spawnWorkspace(dataSource);
			} else {
				setDataSource(dataSource);
				loadUsingEncoding(config.get(Settings.ENCODING));
				content.discardUndoRedo();
				synchronize(false);
			}
			TextMash.updateRecent(dataSource);
		}
	}

	public void reset() {
		setDataSource("");
		content.setText("");
		content.discardUndoRedo();
		synchronize(false);
	}

	public void actionSetLineEnding(String type) {
		((ConcurrentDocument) content.getDocument()).setLineEnding(type);
		config.set(Settings.LINE_ENDING, type);
	}

	public void close(boolean all) {

		if (isDirty()) {

			switch (DialogBox.showDialogBox(this, String.format(
					"Do you want to save the "
							+ "changes you made in\nthe document “%s”?",
					getShortWorkspaceNormalizedTitle()),
					"Your changes will be lost if you don’t save them.", 0, 3,
					"Don't Save", "", "Cancel", "Save")) {
			case 2:
				return;
			case 3:
				actionSave();
			}
		}

		if (TextMash.getWorkspaces().size() > 1) {
			TextMash.disposeWorkspace(this);
			// content.undo.stop();
			dispose();
		} else {
			if (all) {
				TextMash.save();
				System.exit(0);
			} else {
				reset();
			}
		}
	}

	public boolean isUsed() {
		return (content.getText().length() != 0) || !isUnnamed();
	}

	public void setQuickLookup(boolean state) {
		quickLookup = state;
		config.set(Settings.QUICK_LOOKUP, state);
		OccurrencesMatcher.getInstance().refresh();
		// getTextPane().repaint();
	}

	public boolean hasQuickLookup() {
		return quickLookup;
	}

	public String getText() {
		return getTextPane().getTextForSearching(false);
	}

	// public static void changeSelectionCase(JTextComponent textComponent,
	// boolean upper) {
	//
	// String selectedText = textComponent.getSelectedText();
	//
	// if (selectedText == null || selectedText.length() == 0)
	// return;
	//
	// if (upper)
	// selectedText = selectedText.toUpperCase();
	// else
	// selectedText = selectedText.toLowerCase();
	//
	// textComponent.replaceSelection(selectedText);
	//
	// }
	//
	// public void actionDeleteLine() {
	//		    	
	//		    	
	//		    	
	// /*JTextComponent textComponent = content;
	// char newLine = '\n';
	// String _newLine = "\n";
	// int caretIndex = textComponent.getCaretPosition();
	//
	// String text = textComponent.getText();
	// StringBuffer sb = new StringBuffer(text);
	//
	// int endOfLineIndexBefore = -1;
	// int endOfLineIndexAfter = sb.indexOf(_newLine, caretIndex);
	//
	// char[] textChars = text.toCharArray();
	//
	// for (int i = 0; i < textChars.length; i++) {
	//
	// if (i >= caretIndex) {
	// break;
	// }
	//
	// else {
	//
	// if (textChars[i] == newLine)
	// endOfLineIndexBefore = i;
	//
	// }
	//
	// }
	//
	// if (endOfLineIndexBefore == -1) {
	// endOfLineIndexBefore = 0;
	// }
	//
	// if (endOfLineIndexAfter == -1) {
	// sb.delete(endOfLineIndexBefore, sb.length());
	// } else if (endOfLineIndexBefore == -1) {
	// sb.delete(0, endOfLineIndexAfter + 1);
	// } else if (endOfLineIndexBefore == 0 && endOfLineIndexAfter == 0) {
	// sb.deleteCharAt(0);
	// } else {
	// sb.delete(endOfLineIndexBefore, endOfLineIndexAfter);
	// }
	//
	// textComponent.setText(sb.toString());
	// textComponent.setCaretPosition(endOfLineIndexBefore == -1 ? 0 :
	// endOfLineIndexBefore);*/
	//		    	
	//		    	
	// }

}
